/*
 * Copyright (C),2015,北京新诺创科软件技术有限公司
 * author zhangmengliang
 */
package com.xnck.mfpms.service;

import com.xiaoleilu.hutool.DateUtil;
import com.xiaoleilu.hutool.StrUtil;
import com.xnck.mfpms.dao.*;
import com.xnck.mfpms.entity.*;
import com.xnck.mfpms.exception.ValidateException;
import com.xnck.mfpms.util.PagerUtil;
import com.xnck.mfpms.util.ValidatorUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.sql.Criteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class RoleService {

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private ActionDao actionDao;

    @Autowired
    private RuleDao ruleDao;

    @Autowired
    private MenuDao menuDao;

    @Autowired
    private UserRoleDao userRoleDao;

    @Autowired
    private RoleRuleDao roleRuleDao;

    @Autowired
    private RoleActionDao roleActionDao;

    @Autowired
    private RoleMenuDao roleMenuDao;

    public RoleInfo get(String roleId){
        return roleDao.get(roleId);
    }

    public int getsCount(String roleName){
        Criteria cri = Cnd.cri();
        this.getSearchCnd(cri, roleName);
        return roleDao.searchCount(cri);
    }

    public List<RoleInfo> gets(String roleName, String orderName, String orderType,
                               int pageSize, int beginIndex){
        Criteria cri = Cnd.cri();
        this.getSearchCnd(cri, roleName);
        PagerUtil.getSearchOrder(cri, RoleInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return roleDao.searchByPage(cri, currentPage, pageSize);
    }

    /**
     * 创建角色
     * @param creatorId
     * @param displayName
     * @param enable
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void create(String creatorId, String displayName, boolean enable) throws Exception {
        this.checkRoleInsertInput(creatorId, displayName);
        String roleId = UUID.randomUUID().toString();
        Date addTime = DateUtil.date();
        roleDao.insert(roleId, displayName, creatorId, enable, addTime);
    }

    /**
     * 修改角色
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(String id, String displayName, boolean enable) throws Exception {
        this.checkRoleUpdatetInput(displayName);
        RoleInfo role = roleDao.get(id);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        role.setDisplayname(displayName);
        role.setEnable(enable);
        roleDao.update(role);
    }

    /**
     * 批量删除角色
     * @param roleIds
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void deletes(String roleIds) throws Exception {
        roleActionDao.clear(Cnd.where(RoleAction.FIELD_ROLEID, "in", roleIds.split(",")));
        roleRuleDao.clear(Cnd.where(RoleRule.FIELD_ROLEID, "in", roleIds.split(",")));
        roleMenuDao.clear(Cnd.where(RoleMenu.FIELD_ROLEID, "in", roleIds.split(",")));
        userRoleDao.clear(Cnd.where(UserRole.FIELD_ROLEID, "in", roleIds.split(",")));
        roleDao.clear(Cnd.where(RoleInfo.FIELD_ID, "in", roleIds.split(",")));
    }

    /**
     * 已关联人员数量
     * @param roleId 关联的角色id
     * @param userName 人员名称
     * @return
     */
    public int getJoinedUsersCount(String roleId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountJoinRole(roleId, cri);
    }

    /**
     * 未关联人员数量
     * @param roleId 关联的角色id
     * @param userName 人员名称
     * @return
     */
    public int getNoJoinUsersCount(String roleId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountNotJoinRole(roleId, cri);
    }

    /**
     * 已关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getJoinedUsers(String roleId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getJoinRole(roleId, cri, currentPage, pageSize);
    }

    /**
     * 未关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getNoJoinUsers(String roleId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getNotJoinRole(roleId, cri, currentPage, pageSize);
    }

    /**
     * 关联人员和角色
     * @param roleId 被关联角色
     * @param userIdStr 待关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinUser(String roleId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<UserInfo> joiningUsers = userDao.search(Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joiningUsers.size() > 0){
            roleDao.findLink(role, RoleInfo.FIELD_USERS);
            List<UserInfo> users = this.getUserIdNotJoin(joiningUsers, role.getUsers());
            if (users.size() > 0){
                role.setUsers(users);
                roleDao.insertRelation(role, RoleInfo.FIELD_USERS);
            }
        }
    }

    /**
     * 取消关联人员和角色
     * @param roleId 被关联角色
     * @param userIdStr 被关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelJoinUser(String roleId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<UserInfo> joinedUsers = userDao.search(Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joinedUsers.size() > 0){
            List<String> joinedUserIds = new ArrayList<String>();
            for (UserInfo joinedUser : joinedUsers) {
                joinedUserIds.add(joinedUser.getId());
            }
            userRoleDao.clear(Cnd.where(UserRole.FIELD_USERID, "in", joinedUserIds.toArray(new String[joinedUserIds.size()]))
                    .and(UserRole.FIELD_ROLEID, "=", roleId));
        }
    }

    /**
     * 已关联人员数量
     * @param byUserId 操作人
     * @param roleId 关联的角色id
     * @param userName 人员名称
     * @return
     */
    public int getJoinedUsersCount(String byUserId, String roleId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountJoinRoleByRule(byUserId, roleId, cri);
    }

    /**
     * 未关联人员数量
     * @param byUserId 操作人
     * @param roleId 关联的角色id
     * @param userName 人员名称
     * @return
     */
    public int getNoJoinUsersCount(String byUserId, String roleId, String userName){
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        return userDao.getCountNotJoinRoleByRule(byUserId, roleId, cri);
    }

    /**
     * 已关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getJoinedUsers(String byUserId, String roleId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getJoinRoleByRule(byUserId, roleId, cri, currentPage, pageSize);
    }

    /**
     * 未关联人员
     * @return
     * @throws Exception
     */
    public List<UserInfo> getNoJoinUsers(String byUserId, String roleId, String userName,
                                         String orderName, String orderType,
                                         int pageSize, int beginIndex) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        Criteria cri = Cnd.cri();
        if (StrUtil.isNotBlank(userName)) {
            cri.where().andLike(UserInfo.FIELD_DISPLAYNAME, userName);
        }
        PagerUtil.getSearchOrder(cri, UserInfo.FIELD_DISPLAYNAME, orderName, orderType);
        int currentPage = PagerUtil.getCurrentPage(beginIndex, pageSize);
        return userDao.getNotJoinRoleByRule(byUserId, roleId, cri, currentPage, pageSize);
    }

    /**
     * 关联人员和角色
     * @param byUserId 操作者
     * @param roleId 被关联角色
     * @param userIdStr 待关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinUser(String byUserId, String roleId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<UserInfo> joiningUsers = userDao.getUsersByRule(byUserId, Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joiningUsers.size() > 0){
            roleDao.findLink(role, RoleInfo.FIELD_USERS);
            List<UserInfo> users = this.getUserIdNotJoin(joiningUsers, role.getUsers());
            if (users.size() > 0){
                role.setUsers(users);
                roleDao.insertRelation(role, RoleInfo.FIELD_USERS);
            }
        }
    }

    /**
     * 取消关联人员和角色
     * @param byUserId 操作者
     * @param roleId 被关联角色
     * @param userIdStr 被关联人员列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelJoinUser(String byUserId, String roleId, String userIdStr) throws Exception {
        if (StrUtil.isBlank(userIdStr)){
            throw new ValidateException("用户不存在");
        }
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<UserInfo> joinedUsers = userDao.getUsersByRule(byUserId, Cnd.where(UserInfo.FIELD_ID, "in", userIdStr.split(",")));
        if (joinedUsers.size() > 0){
            List<String> joinedUserIds = new ArrayList<String>();
            for (UserInfo joinedUser : joinedUsers) {
                joinedUserIds.add(joinedUser.getId());
            }
            userRoleDao.clear(Cnd.where(UserRole.FIELD_USERID, "in", joinedUserIds.toArray(new String[joinedUserIds.size()]))
                    .and(UserRole.FIELD_ROLEID, "=", roleId));
        }
    }

    /**
     * 角色与权限的关联关系
     * @param roleId
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> getActionNodes(String roleId) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<ActionInfo> actions = actionDao.search(Cnd.orderBy().asc(ActionInfo.FIELD_DISPLAYNAME));
        roleDao.findLink(role, RoleInfo.FIELD_ACTIONS);
        List<Map<String, Object>> nodes = new ArrayList<Map<String, Object>>();
        for (ActionInfo action: actions) {
            Map<String, Object> node = new HashMap<String, Object>();
            node.put("id", action.getId());
            node.put("name", action.getDisplayname());
            node.put("pId", action.getParentid());
            boolean isChecked = false;
            for (ActionInfo joinAction : role.getActions()) {
                if (joinAction.getId().equals(action.getId())){
                    isChecked = true;
                }
            }
            node.put("checked", isChecked);
            nodes.add(node);
        }
        return nodes;
    }

    /**
     * 关联角色和权限
     * @param roleId 被关联角色
     * @param actionIdStr 被关联权限列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinAction(String roleId, String actionIdStr) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        if (StrUtil.isBlank(actionIdStr)){
            throw new ValidateException("权限不存在");
        }
        String[] actionIds = actionIdStr.split(",");
        roleDao.clearLinks(role, RoleInfo.FIELD_ACTIONS);
        List<ActionInfo> actions = actionDao.search(Cnd.where(ActionInfo.FIELD_ID, "in", actionIds));
        if (actions.size() > 0){
            role.setActions(actions);
            roleDao.insertRelation(role, RoleInfo.FIELD_ACTIONS);
        }
    }

    /**
     * 角色与规则的关联关系
     * @param roleId
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> getRuleNodes(String roleId) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<RuleInfo> rules = ruleDao.search(Cnd.orderBy().asc(RuleInfo.FIELD_DISPLAYNAME));
        roleDao.findLink(role, RoleInfo.FIELD_RULES);
        List<Map<String, Object>> nodes = new ArrayList<Map<String, Object>>();
        for (RuleInfo rule: rules) {
            Map<String, Object> node = new HashMap<String, Object>();
            node.put("id", rule.getId());
            node.put("name", rule.getDisplayname());
            node.put("pId", rule.getParentid());
            boolean isChecked = false;
            for (RuleInfo joinRule : role.getRules()) {
                if (joinRule.getId().equals(rule.getId())){
                    isChecked = true;
                }
            }
            node.put("checked", isChecked);
            nodes.add(node);
        }
        return nodes;
    }

    /**
     * 关联角色和规则
     * @param roleId 被关联角色
     * @param ruleIdStr 被关联规则列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinRule(String roleId, String ruleIdStr) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        if (StrUtil.isBlank(ruleIdStr)){
            throw new ValidateException("权限不存在");
        }
        String[] ruleIds = ruleIdStr.split(",");
        roleDao.clearLinks(role, UserInfo.FIELD_RULES);
        List<RuleInfo> rules = ruleDao.search(Cnd.where(RoleInfo.FIELD_ID, "in", ruleIds));
        if (rules.size() > 0){
            role.setRules(rules);
            roleDao.insertRelation(role, RoleInfo.FIELD_RULES);
        }
    }

    /**
     * 角色与菜单的关联关系
     * @param roleId
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> getMenuNodes(String roleId) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        List<MenuInfo> menus = menuDao.search(Cnd.orderBy().asc(MenuInfo.FIELD_ORDERNO));
        roleDao.findLink(role, UserInfo.FIELD_MENUS);
        List<Map<String, Object>> nodes = new ArrayList<Map<String, Object>>();
        for (MenuInfo menu: menus) {
            Map<String, Object> node = new HashMap<String, Object>();
            node.put("id", menu.getId());
            node.put("name", menu.getDisplayname());
            node.put("pId", menu.getParentid());
            boolean isChecked = false;
            for (MenuInfo joinMenu : role.getMenus()) {
                if (joinMenu.getId().equals(menu.getId())){
                    isChecked = true;
                }
            }
            node.put("checked", isChecked);
            nodes.add(node);
        }
        return nodes;
    }

    /**
     * 关联角色和菜单
     * @param roleId 被关联菜单
     * @param menuIdStr 被关联菜单列表(以逗号相隔)
     */
    @Transactional(rollbackFor = Exception.class)
    public void joinMenu(String roleId, String menuIdStr) throws Exception {
        RoleInfo role = roleDao.get(roleId);
        if (null == role){
            throw new ValidateException("角色不存在");
        }
        if (StrUtil.isBlank(menuIdStr)){
            throw new ValidateException("菜单不存在");
        }
        String[] menuIds = menuIdStr.split(",");
        roleDao.clearLinks(role, RoleInfo.FIELD_MENUS);
        List<MenuInfo> menus = menuDao.search(Cnd.where(MenuInfo.FIELD_ID, "in", menuIds));
        if (menus.size() > 0){
            role.setMenus(menus);
            roleDao.insertRelation(role, RoleInfo.FIELD_MENUS);
        }
    }

    private void getSearchCnd(Criteria cri, String roleName){
        if (StrUtil.isNotBlank(roleName)){
            cri.where().and(
                    Cnd.exps(RoleInfo.FIELD_DISPLAYNAME, "like", "%" + roleName + "%"));
        }
    }

    /**
     * 获得传入的ID中未关联的人员
     */
    private List<UserInfo> getUserIdNotJoin(List<UserInfo> joiningUsers, List<UserInfo> joinedUsers){
        List<UserInfo> users = new ArrayList<UserInfo>();
        for (UserInfo joiningUser : joiningUsers) {
            boolean isIn = false;
            for (UserInfo joinedUser : joinedUsers) {
                if (joinedUser.getId().equals(joiningUser.getId())){
                    isIn = true;
                    break;
                }
            }
            if (!isIn){
                users.add(joiningUser);
            }
        }
        return users;
    }

    private void checkRoleInsertInput(String creatorId, String displayName) throws Exception {
        if (ValidatorUtils.isEmpty(creatorId)){
            throw new ValidateException("创建者不存在");
        }
        if (ValidatorUtils.isEmpty(displayName)){
            throw new ValidateException("显示名不能为空");
        }
        if(displayName.getBytes().length < 4 || displayName.getBytes().length > 20){
            throw new ValidateException("显示名应由4-20个字符组成");
        }
    }

    private void checkRoleUpdatetInput(String displayName) throws Exception {
        if (ValidatorUtils.isEmpty(displayName)){
            throw new ValidateException("显示名不能为空");
        }
        if(displayName.getBytes().length < 4 || displayName.getBytes().length > 20){
            throw new ValidateException("显示名应由4-20个字符组成");
        }
    }
}
